package com.rxh.risk;

import com.rxh.cache.RiskQuotaCache;
import com.rxh.cache.RiskQuotaDataCache;
import com.rxh.cache.SysConstantCache;
import com.rxh.exception.PayException;
import com.rxh.pojo.risk.RiskQuota;
import com.rxh.pojo.risk.RiskQuotaData;
import com.rxh.pojo.sys.SysConstant;
import com.rxh.service.RiskService;
import com.rxh.utils.StringUtils;
import com.rxh.utils.SystemConstant;
import com.rxh.utils.UUID;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Created with IntelliJ IDEA.
 * User: 王德明
 * Date: 2018/9/11
 * Time: 16:08
 * Project: Management_new
 * Package: com.rxh.risk
 */
@Component
public class QuotaComponent implements RiskInterface {

    @Autowired
    private RiskQuotaDataCache riskQuotaDataCache;
    @Autowired
    private RiskQuotaCache riskQuotaCache;
    @Autowired
    private SysConstantCache sysConstantCache;
    @Resource
    private RiskService riskService;

    @Override
    public void analysis(RiskContext riskContext) throws PayException {
        Integer webSiteId = riskContext.getMerchantWebSite().getId();
        Integer merId = riskContext.getTradeObject().getMerNo();
        Integer acquirerId = riskContext.getCoreAcquirer().getId();
        String payType = riskContext.getCoreOrder().getPayType();
        List<RiskQuota> quotaList = riskQuotaCache.getQuota(webSiteId, merId, acquirerId, payType);
        List<RiskQuotaData> quotaDataList = riskQuotaDataCache.getQuotaData(webSiteId, merId, acquirerId, payType);
        Map<Short, List<RiskQuotaData>> riskQuotaDataMap = quotaDataList.stream().collect(Collectors.groupingBy(RiskQuotaData::getRefType));
        for (RiskQuota rq : quotaList) {
            quotaTodo(riskContext, riskQuotaDataMap.get(rq.getRefType()), rq);
        }
        // 分析风控限额数据对象，判断是否有缺失对象
        analysisRiskQuotaData(quotaDataList, riskQuotaDataMap, webSiteId, merId, acquirerId);
        updateRiskQuotaData(quotaDataList, payType, riskContext.getCoreOrder().getRmbAmount());
    }

    @Override
    public String getRiskName() {
        return SystemConstant.RISK_QUOTA;
    }

    /**
     * 分析风控限额数据对象，判断是否有缺失对象
     *
     * @param quotaDataList 当前风控限额数据
     * @param webSiteId     商户网址Id
     * @param merId         商户Id
     * @param acquirerId    收单机构Id
     */
    private void analysisRiskQuotaData(List<RiskQuotaData> quotaDataList, Map<Short, List<RiskQuotaData>> riskQuotaDataMap, Integer webSiteId, Integer merId, Integer acquirerId) {
        // 获取信用卡系统常量
        List<SysConstant> credit = sysConstantCache.getConstantByGroupName(SystemConstant.CREDIT);
        // 假如QuotaData为空（如：新的一年）遍历生成所有相应refType、type、payType对象。
        if (quotaDataList.size() == 0) {
            for (short i = 0; i < RiskQuota.NUMBER_OF_REF_TYPE; i++) {
                // 执行根据refType循环插入方法
                switchRefType(quotaDataList, credit, i, 0, webSiteId, merId, acquirerId);
            }
        } else {
            // 生成以所有refType值为value的List对象，用于判断quotaDataList是否
            List<Short> refTypes = Arrays.asList(RiskQuota.REF_TYPE_COMMON, RiskQuota.REF_TYPE_ACQUIRER_ID, RiskQuota.REF_TYPE_MERCHANT_ID, RiskQuota.REF_TYPE_SITE_ID);
            // 若不为空则以refType为key生成Map<Short, List<RiskQuotaData>>对象
            for (Map.Entry<Short, List<RiskQuotaData>> entry :
                    riskQuotaDataMap.entrySet()) {
                /*
                将当前map存在的refType从refTypes中去除，当map循环结束时，剩下的refTypes值为quotaDataList中不存在的refType值
                如：quotaDataList中含有re'fType为0（通用）与1（收单机构），但不含有2（商户Id）、3（商户网址Id）时，map循环结束时
                refTypes中就会剩下2（商户Id）、3（商户网址Id）两个值，说明quotaDataList中不含有refType为：2（商户Id）、3（商户网址Id）的riskQuotaData对象。
                 */
                refTypes = refTypes.stream().filter(aShort -> !aShort.equals(entry.getKey())).collect(Collectors.toList());
                // 当前refType类型下List<RiskQuotaData>>.size() 若为0则缺失日月年，若为1则缺失日月，若为2则缺失日，若为3则不缺失
                if (entry.getValue().size() < RiskQuota.NUMBER_OF_LIMIT_TYPE) {
                    switchRefType(quotaDataList, credit, entry.getKey(), entry.getValue().size(), webSiteId, merId, acquirerId);
                }
            }
            // 循环quotaDataList中不存在的refType类型
            if (refTypes.size() != 0) {
                refTypes.forEach(refType -> {
                    switchRefType(quotaDataList, credit, refType, 0, webSiteId, merId, acquirerId);
                });
            }
        }
    }

    /**
     * 根据限定类型执行插入方法
     *
     * @param quotaDataList 当前风控限额数据
     * @param credit        信用卡系统常量
     * @param refType       限定类型
     * @param size          若为0则缺失日月年，若为1则缺失日月，若为2则缺失日，若为3则不缺失
     * @param webSiteId     商户网址Id
     * @param merId         商户Id
     * @param acquirerId    收单机构Id
     */
    private void switchRefType(List<RiskQuotaData> quotaDataList, List<SysConstant> credit, Short refType, int size, Integer webSiteId, Integer merId, Integer acquirerId) {
        // 判断传入refType，执行doAddRiskQuotaData方法
        switch (refType) {
            case RiskQuota.REF_TYPE_COMMON:
                addRiskQuotaData(quotaDataList, credit, refType, size, 0);
                break;
            case RiskQuota.REF_TYPE_ACQUIRER_ID:
                addRiskQuotaData(quotaDataList, credit, refType, size, acquirerId);
                break;
            case RiskQuota.REF_TYPE_MERCHANT_ID:
                addRiskQuotaData(quotaDataList, credit, refType, size, merId);
                break;
            case RiskQuota.REF_TYPE_SITE_ID:
                addRiskQuotaData(quotaDataList, credit, refType, size, webSiteId);
                break;
            default:
                break;
        }
    }

    /**
     * 向当前风控限额数据假如缺失对象
     *
     * @param quotaDataList 当前风控限额数据
     * @param credit        信用卡系统常量
     * @param refType       限定类型
     * @param size          若为0则缺失日月年，若为1则缺失日月，若为2则缺失日，若为3则不缺失
     * @param refId         refType类型Id
     */
    private void addRiskQuotaData(List<RiskQuotaData> quotaDataList, List<SysConstant> credit, Short refType, int size, Integer refId) {
        // 判断传入list大小执行新增操作
        short times = (short) (RiskQuota.NUMBER_OF_LIMIT_TYPE - size);
        /*
        i从1开始，i刚好等于riskQuotaData的type（RiskQuota对象的limitType）
        若size为3说明不缺对象，for循环不执行
        若size为2说明缺失日，i刚好为RiskQuota.LIMIT_TYPE_DAY，for循环执行一次
        若size为2说明缺失日月，i为1时i=RiskQuota.LIMIT_TYPE_DAY，i为2时i=RiskQuota.LIMIT_TYPE_MONTH，for循环执行两次，
        若size为2说明缺失日月年，i为1时i=RiskQuota.LIMIT_TYPE_DAY，i为2时i=RiskQuota.LIMIT_TYPE_MONTH，i为3时i=RiskQuota.LIMIT_TYPE_YEAR，for循环执行三次，
         */
        for (short i = 1; i <= times; i++) {
            // 无论是何种payTpe交易都新增对应RiskQuotaData对象
            for (SysConstant s :
                    credit) {
                RiskQuotaData r = new RiskQuotaData();
                r.setId(UUID.createKey("risk_quota_data"));
                r.setRefId(refId);
                r.setRefType(refType);
                // i刚好为RiskQuotaData的type值（RiskQuota对象的limitType）
                r.setType(i);
                r.setPayType(s.getFirstValue());
                r.setTradeTime(new Date());
                quotaDataList.add(r);
            }
        }
    }


    private void quotaTodo(RiskContext riskContext, List<RiskQuotaData> quotaDataList, RiskQuota riskQuota) throws PayException {
        // 当前交易人民币金额
        BigDecimal rmbAmount = riskContext.getCoreOrder().getRmbAmount();
        // 限定额度
        BigDecimal quotaAmount = new BigDecimal(riskQuota.getAmount());
        // 每笔额度
        if (riskQuota.getLimitType() == RiskQuota.LIMIT_TYPE_ORDER_AMOUNT) {
            if (isExceed(rmbAmount, quotaAmount)) {
                throw new PayException("超出单笔最大限额：" + riskQuota.getAmount() + "，当前交易人民币金额：" + rmbAmount, 2004);
            }
        }
        // 提醒内容
        String msg = SystemConstant.REF_TYPE[riskQuota.getRefType()] +
                (riskQuota.getRefType() == SystemConstant.REF_TYPE_ACQUIRER ? "：" + riskContext.getCoreAcquirer().getName() : "") +
                "（" + riskQuota.getRefType() + "）" +
                "（" + riskContext.getCoreOrder().getPayType() + "）";
        if (quotaDataList == null) {
            return;
        }
        List<RiskQuotaData> quotaDataListNew = quotaDataList
                .stream()
                .filter(riskQuotaData -> riskQuota.getLimitType().equals(riskQuotaData.getType()))
                .filter(riskQuotaData -> isExceed(riskQuotaData.getAmount(), quotaAmount)).collect(Collectors.toList());
        for (RiskQuotaData riskQuotaData : quotaDataListNew) {
            // 超出限定额度的值
            BigDecimal excessAmount = riskQuotaData.getAmount().add(rmbAmount).subtract(new BigDecimal(riskQuota.getAmount()));
            switch (riskQuota.getLimitType()) {
                case RiskQuota.LIMIT_TYPE_DAY:
                    throw new PayException(msg + "每日最大限额：" + riskQuota.getAmount() + "，超出：" + excessAmount, 2004);
                case RiskQuota.LIMIT_TYPE_MONTH:
                    throw new PayException(msg + "每月最大限额：" + riskQuota.getAmount() + "，超出：" + excessAmount, 2004);
                case RiskQuota.LIMIT_TYPE_YEAR:
                    throw new PayException(msg + "每年最大限额：" + riskQuota.getAmount() + "，超出：" + excessAmount, 2004);
                default:
                    break;
            }
        }
    }

    private boolean isExceed(BigDecimal amount, BigDecimal target) {
        return amount.compareTo(target) > 0;
    }

    private void updateRiskQuotaData(List<RiskQuotaData> quotaDataList, String payType, BigDecimal amount) {
        quotaDataList.forEach(riskQuotaData -> {
            if (StringUtils.equalsIgnoreCase(riskQuotaData.getPayType(), payType)) {
                riskQuotaData.setAmount(riskQuotaData.getAmount() == null ? amount : riskQuotaData.getAmount().add(amount));
            }
        });
        riskService.insertOrUpdateRiskQuotaData(quotaDataList);
    }
}




